unit uCxtSession;

interface

uses
  SysUtils, Classes, uDzThread, uDZPersistent, uDZHashTable, uDZTrace,
  uCxtConst, uCxtTypes, uAppLog;

type
  TCxtSession = class(TDzPersistent)
  private
    fUserName: string;
    fRealName: string;
    fBytesRead: Int64;
    fBytesWritten: Int64;
    fUserID: Integer;
    fSessionID: AnsiString;
    fMinorVersion: Integer;
    fMajorVersion: Integer;
    fBuildNumber: Integer;
    fReleaseNumber: Integer;
    fComputerID: AnsiString;
    fOemName: WideString;
    fLocPoint: Integer;
    fSmsPoint: Integer;
    fUserState: TUserState;
    fIP: LongWord;
  published
    property SessionID: AnsiString read fSessionID write fSessionID;
    property IP: LongWord read fIP write fIP;
    property OemName: WideString read fOemName write fOemName;
    property MajorVersion: Integer read fMajorVersion write fMajorVersion;
    property MinorVersion: Integer read fMinorVersion write fMinorVersion;
    property ReleaseNumber: Integer read fReleaseNumber write fReleaseNumber;
    property BuildNumber: Integer read fBuildNumber write fBuildNumber;
    property UserID: Integer read fUserID write fUserID;
    property UserName: string read fUserName write fUserName;
    property RealName: string read fRealName write fRealName;
    property UserState: TUserState read fUserState write fUserState;
    property LocPoint: Integer read fLocPoint write fLocPoint;
    property SmsPoint: Integer read fSmsPoint write fSmsPoint;
    property BytesRead: Int64 read fBytesRead write fBytesRead;
    property BytesWritten: Int64 read fBytesWritten write fBytesWritten;
    property ComputerID: AnsiString read fComputerID write fComputerID;
  end;

function AddSession(IP: LongWord; const UserName: AnsiString; OemName,
  RealName: WideString; MajorVersion, MinorVersion, ReleaseNumber,
  BuildNumber, UserID, LocPoint, SmsPoint: Integer;
  UserState: TUserState): TCxtSession;
  
procedure RemoveSession(const SessionID: AnsiString);

function GetSession(const SessionID: AnsiString): TCxtSession;

var
  SessionTable: TDzAnsiStrHashTable;

implementation

var
  SessionTableLock: TDzCriticalSection;

function RandomAnsiString(CharTable: PAnsiChar; CharCount: Integer;
  ResultLength: Integer): AnsiString;
var
  i: Integer;
begin
  SetLength(Result, ResultLength);
  for i := 1 to ResultLength do
    Result[i] := CharTable[Random(CharCount)];
end;

function GenerateSessionID: AnsiString;
const
  CharTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
begin
  while True do
  begin
    Result := RandomAnsiString(CharTable, Length(CharTable), 16);
    if not SessionTable.Find(Result, nil) then Break;
  end;
end;
  
function AddSession(IP: LongWord; const UserName: AnsiString; OemName,
  RealName: WideString; MajorVersion, MinorVersion, ReleaseNumber,
  BuildNumber, UserID, LocPoint, SmsPoint: Integer;
  UserState: TUserState): TCxtSession;
begin
  FmtWriteLog(tsDebug, SLogUserLogin, [string(UserName), string(OemName),
    MajorVersion, MinorVersion, ReleaseNumber, BuildNumber]);

  SessionTableLock.Acquire;
  try
    Result := TCxtSession.Create;
    Result.SessionID := GenerateSessionID;
    Result.IP := IP;
    Result.UserName := UserName;
    Result.OemName := OemName;
    Result.RealName := RealName;
    Result.MajorVersion := MajorVersion;
    Result.MinorVersion := MinorVersion;
    Result.ReleaseNumber := ReleaseNumber;
    Result.BuildNumber := BuildNumber;
    Result.UserID := UserID;
    Result.LocPoint := LocPoint;
    Result.SmsPoint := SmsPoint;
    Result.UserState := UserState;
    SessionTable.Insert(Result.SessionID, Result, nil, False);
  finally
    SessionTableLock.Release;
  end;
end;

procedure RemoveSession(const SessionID: AnsiString);
var
  Session: TCxtSession;
begin
  if SessionTable.Find(SessionID, @Session) then
  begin
    FmtWriteLog(tsInformation, '%s logout'#10, [Session.UserName]);
    Session.Free;
  end;
  SessionTableLock.Acquire;
  try
    SessionTable.Delete(SessionID);
  finally
    SessionTableLock.Release;
  end;
end;

function GetSession(const SessionID: AnsiString): TCxtSession;
begin 
  SessionTableLock.Acquire;
  try
    if not SessionTable.Find(SessionID, @Result) then Result := nil;
  finally
    SessionTableLock.Release;
  end;  
end;

procedure ClearSession;
var
  Iterator: TDzAnsiStrHashTableIterator;
begin
  SessionTableLock.Acquire;
  try
    Iterator.SetTable(SessionTable);
    while not Iterator.Eof do
    begin
      Iterator.Value.Free; 
      Iterator.Inc;
    end;
    SessionTable.Clear;
  finally
    SessionTableLock.Release;
  end;
end;
  
initialization
  SessionTableLock := TDzCriticalSection.Create(100);
  SessionTable := TDzAnsiStrHashTable.Create(CalcHashTableSize(500));

finalization
  ClearSession;
  SessionTable.Free;  
  SessionTableLock.Free;
end.
